﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace WpfBeyond
{
    public class IOCP_SOCK
    {
        public Socket socket;
        public DateTime heartbeat_time;
        public string ipaddr;
    }

    public class IOCP
    {
        /// <summary>
        /// // 监听Socket，用于接受客户端的连接请求
        /// </summary>
        public Socket Socketlistener;
        /// <summary>
        /// // 用于服务器执行的互斥同步对象
        /// </summary>
        private static Mutex mutex = new Mutex();
        //完成端口上进行投递所用的IoContext对象池
        //private IoContextPool ioContextPool;
        // 
        /// <summary>
        /// 服务器上连接的客户端总数
        /// </summary>
        private Int32 numConnectedSockets;
        /// <summary>
        /// 服务器能接受的最大连接数量
        /// </summary>
        private Int32 numConnections = 100;
        public List<IOCP_SOCK> sockets = new List<IOCP_SOCK>();
        private object _lock = new object();
        /// <summary>
        /// 用于每个I/O Socket操作的缓冲区大小
        /// </summary>
        private Int32 bufferSize = 64 * 1024;
        /// <summary>
        /// 端口
        /// </summary>
        private Int32 bufferPort = 23023;
        //ip
        //private String _GetAddress = "127.0.0.1";
        //所有设备用户信息
        //private List<Equipment> ListInfo = new List<Equipment>();
        //接收到数据事件
        public delegate void RecvDataHandler(Socket socket, byte[] data, int len);
        public event RecvDataHandler RecvCallBack;

        public delegate void CloseSocketHandler(Socket socket);
        public event CloseSocketHandler CloseSocketCallBack;

        //完成端口上进行投递所用的IoContext对象池
        private IoContextPool ioContextPool;
        DateTime GetDate;        

        string iocp_name = "";

        Queue<Socket> heartbeat_socket = new Queue<Socket>(100);        

        public IOCP(string name, Int32 Port)
        {
            iocp_name = name;
            //获取所有设备用户信息
            //ListInfo = AdoGetInfo.GetEquipmentUser();
            GetDate = DateTime.Now.AddHours(-1);

            this.numConnectedSockets = 0;

            this.bufferPort = Port;

            this.ioContextPool = new IoContextPool(numConnections);

            // 为IoContextPool预分配SocketAsyncEventArgs对象
            for (Int32 i = 0; i < this.numConnections; i++)
            {
                SocketAsyncEventArgs ioContext = new SocketAsyncEventArgs();
                ioContext.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted);
                ioContext.SetBuffer(new Byte[this.bufferSize], 0, this.bufferSize);
                //   将预分配的对象加入SocketAsyncEventArgs对象池中
                this.ioContextPool.Add(ioContext);
            }
            // 获得主机相关信息
            //IPAddress[] addressList = Dns.GetHostEntry(Environment.MachineName).AddressList;
            //IPEndPoint localEndPoint = new IPEndPoint(addressList[addressList.Length - 1], bufferPort);
            //监听所有本地地址
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, bufferPort);

            // 创建监听socket
            this.Socketlistener = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            // 设置缓存
            this.Socketlistener.ReceiveBufferSize = this.bufferSize;
            this.Socketlistener.SendBufferSize = this.bufferSize;

            //IPv6 绑定
            if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
            {
                // 配置监听socket为 dual-mode (IPv4 & IPv6) 
                // 27 is equivalent to IPV6_V6ONLY socket option in the winsock snippet below,
                this.Socketlistener.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
                this.Socketlistener.Bind(new IPEndPoint(IPAddress.IPv6Any, localEndPoint.Port));
            }
            else//IPv4 绑定
            {
                this.Socketlistener.Bind(localEndPoint);
            }

            // 开始监听
            this.Socketlistener.Listen(this.numConnections);

            // 在监听Socket上投递一个接受请求。
            this.StartAccept(null);

            // Blocks the current thread to receive incoming messages.
            mutex.WaitOne();

            Thread thread5 = new Thread(new ThreadStart(Thread_Monitor));
            thread5.IsBackground = true;
            thread5.Start();
        }

        private void Thread_Monitor()
        {
            int heartbeat_count = 0;
            Socket s = null;
            DateTime dt2 = DateTime.Now;
            int index = 0;
            while (true)
            {
                try
                {
                    heartbeat_count = heartbeat_socket.Count;
                    if (heartbeat_count > 0)
                    {
                        heartbeat_count--;

                        s = heartbeat_socket.Dequeue();

                        if (s == null) continue;

                        lock (_lock)
                        {
                            for (int i = 0; i < sockets.Count; i++)
                            {
                                if (s == sockets[i].socket)
                                {
                                    sockets[i].heartbeat_time = DateTime.Now;
                                }
                            }
                        }
                    }

                    if (dt2.Subtract(DateTime.Now).Duration().TotalSeconds > 5 && sockets.Count > 0)
                    {
                        dt2 = DateTime.Now;

                        lock (_lock)
                        {
                            for (index = sockets.Count - 1; index >= 0; index--)
                            {
                                /*客户端心跳30秒发一次，60秒没心跳客户端就掉了*/
                                if (sockets[index].heartbeat_time.Subtract(DateTime.Now).Duration().TotalSeconds > 90)
                                {
                                    string outStr = String.Format("{0} {1} 90秒没心跳，服务器主动断开。",
                                        iocp_name,
                                        sockets[index].ipaddr.ToString());
                                    LogHelp.Error(outStr);
                                    if (CloseSocketCallBack != null)
                                    {
                                        CloseSocketCallBack(sockets[index].socket);
                                    }
                                    sockets[index].socket.Close();
                                    sockets.RemoveAt(index);
                                    break;
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelp.Error(ex.ToString());
                }

                Thread.Sleep(1000);
            }
        }

        /// <summary>
        /// 监听Socket接受处理
        /// </summary>
        /// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param>
        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            Socket s = e.AcceptSocket;
            if (s.Connected)
            {
                try
                {
                    SocketAsyncEventArgs ioContext = this.ioContextPool.Pop();

                    if (ioContext != null)
                    {
                        // 从接受的客户端连接中取数据配置ioContext
                        //ioContext.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted);
                        //byte [] by=new Byte[]{};
                        //ioContext.SetBuffer(by, 0, by.Length);

                        // 从接受的客户端连接中取数据配置ioContext
                        ioContext.UserToken = s;

                        Interlocked.Increment(ref this.numConnectedSockets);
                        lock (_lock)
                        {
                            IOCP_SOCK sock = new IOCP_SOCK();
                            sock.socket = s;
                            sock.heartbeat_time = DateTime.Now;
                            sock.ipaddr = s.RemoteEndPoint.ToString();
                            sockets.Add(sock);
                        }

                        string outStr = String.Format("{0} {1} 连入, 共有 {2} 个连接。",
                            iocp_name,
                            s.RemoteEndPoint.ToString(),
                            this.numConnectedSockets);

                        LogHelp.Info(outStr);

                        if (!s.ReceiveAsync(ioContext))
                        {
                            this.ProcessReceive(ioContext);
                        }
                    }
                    else        //已经达到最大客户连接数量，在这接受连接，发送“连接已经达到最大数”，然后断开连接
                    {
                        s.Send(Encoding.Default.GetBytes("连接已经达到最大数!"));
                        string outStr = String.Format("{0} 连接已满，拒绝 {1} 的连接。", iocp_name, s.RemoteEndPoint);

                        LogHelp.Error(outStr);

                        s.Close();
                    }
                }
                catch (SocketException ex)
                {
                    Socket token = e.UserToken as Socket;
                    string outStr = String.Format("接收 {0} {1} 数据出错, 异常信息：{2} 。",
                        iocp_name, token.RemoteEndPoint, ex.ToString());
                    LogHelp.Error(outStr);
                }
                catch (Exception ex)
                {
                    LogHelp.Error(ex.ToString());
                }
                // 投递下一个接受请求
                this.StartAccept(e);
            }
        }
        /// <summary>
        /// 从客户端开始接受一个连接操作
        /// </summary>
        /// <param name="acceptEventArg">The context object to use when issuing 
        /// the accept operation on the server's listening socket.</param>
        private void StartAccept(SocketAsyncEventArgs acceptEventArg)
        {
            if (acceptEventArg == null)
            {
                acceptEventArg = new SocketAsyncEventArgs();
                acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
            }
            else
            {
                // 重用前进行对象清理
                acceptEventArg.AcceptSocket = null;
            }

            if (!this.Socketlistener.AcceptAsync(acceptEventArg))
            {
                this.ProcessAccept(acceptEventArg);
            }
        }

        /// <summary>
        ///接收完成时处理函数
        /// </summary>
        /// <param name="e">与接收完成操作相关联的SocketAsyncEventArg对象</param>
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            try
            {
                // 检查远程主机是否关闭连接
                if (e.BytesTransferred > 0)
                {
                    if (e.SocketError == SocketError.Success)
                    {
                        Socket s = (Socket)e.UserToken;

                        if (s.Available > 0)
                        {                            
                            //ClientInformation client = Hdate.AddClient(ListInfo, s.RemoteEndPoint.ToString(), e.BytesTransferred, e.Buffer);
                            string outStr = String.Format("{0}, IP {1}, 接收 {2} BYTE，还有 {3} BYTE。", iocp_name,
                                s.RemoteEndPoint.ToString(), e.BytesTransferred, s.Available);

                            LogHelp.Error(outStr);

                            //wangm:2021-2-3 本次接收的要发送给客户端
                            if (RecvCallBack != null)
                            {
                                RecvCallBack(s, e.Buffer, e.BytesTransferred);
                            }
                            Array.Clear(e.Buffer, 0, bufferSize);
                        }

                        //判断所有需接收的数据是否已经完成
                        if (s.Available == 0)
                        {
                            IPEndPoint localEp = s.RemoteEndPoint as IPEndPoint;
                            // 设置发送数据
                            //byte[] _endRead = new byte[client.Transferred];

                            //bool isClose = false;
                            //client = Hdate.GetClient(ListInfo, client);
                            //string strText = client.Rend;  //Encoding.UTF8.GetString(e.Buffer, 0, client.Transferred);

                            //有数据就当做心跳处理
                            heartbeat_socket.Enqueue(s);
                            //byte[] data = new byte[1024];
                            //e.SetBuffer(data, e.Offset, data.Length);
                            //初始化

                            if (RecvCallBack != null)
                            {
                                RecvCallBack(s, e.Buffer, e.BytesTransferred);
                            }
                            //情况缓冲区
                            //e.SetBuffer(0, bufferSize);
                            //if (s.Available == 0)                            
                            Array.Clear(e.Buffer, 0, bufferSize);                            

                            //try
                            //{
                            //    if (!s.SendAsync(e))        //投递发送请求，这个函数有可能同步发送出去，这时返回false，并且不会引发SocketAsyncEventArgs.Completed事件
                            //    {
                            //        // 同步发送时处理发送完成事件
                            //        this.ProcessSend(e, isClose);
                            //    }

                            //}
                            //catch (Exception ex)
                            //{
                            //    //AdoInsertTemp.AddServerErrorLog("接收完成时处理函数:[IcopServer代码行号330]" + ex.Message);
                            //}
                            if (s.Connected == true)
                            {
                                if (!s.ReceiveAsync(e))    //为接收下一段数据，投递接收请求，这个函数有可能同步完成，这时返回false，并且不会引发SocketAsyncEventArgs.Completed事件
                                {
                                    // 同步接收时处理接收完成事件
                                    this.ProcessReceive(e);
                                }
                            }
                            else
                            {
                                this.CloseClientSocket(e);
                            }

                        }
                        else if (!s.ReceiveAsync(e))    //为接收下一段数据，投递接收请求，这个函数有可能同步完成，这时返回false，并且不会引发SocketAsyncEventArgs.Completed事件
                        {
                            // 同步接收时处理接收完成事件
                            this.ProcessReceive(e);
                        }
                    }
                    else
                    {
                        this.ProcessError(e);
                    }
                }
                else
                {
                    //reback re = new reback();
                    //re.tcp_socket = (Socket)e.UserToken;
                    //re.cmd_type = "CLOSE";
                    //FrmMain.re_tcp_que.Enqueue(re);

                    this.CloseClientSocket(e);
                }
            }
            catch (Exception ex)
            {
                LogHelp.Error(ex.ToString());
            }
        }
        /// <summary>
        /// 发送完成时处理函数
        /// </summary>
        /// <param name="e">与发送完成操作相关联的SocketAsyncEventArg对象</param>
        private void ProcessSend(SocketAsyncEventArgs e, bool isReceive)
        {
            try
            {
                if (e.SocketError == SocketError.Success)
                {
                    Socket s = (Socket)e.UserToken;
                    if (s != null)
                    {
                        //this.CloseClientSocket(s, e);
                        //接收时根据接收的字节数收缩了缓冲区的大小，因此投递接收请求时，恢复缓冲区大小

                        //e.SetBuffer(new Byte[buffer_Size], 0, buffer_Size);

                        e.SetBuffer(0, bufferSize);

                        if (!s.ReceiveAsync(e))     //投递接收请求
                        {
                            // 同步接收时处理接收完成事件
                            this.ProcessReceive(e);
                        }

                    }
                }
                else
                {
                    this.ProcessError(e);
                }
            }
            catch (Exception ex)
            {
                this.ProcessError(e);
                LogHelp.Error(ex.ToString());
            }
        }
        /// <summary>
        /// 当Socket上的发送或接收请求被完成时，调用此函数
        /// </summary>
        /// <param name="sender">激发事件的对象</param>
        /// <param name="e">与发送或接收完成操作相关联的SocketAsyncEventArg对象</param>
        private void OnIOCompleted(object sender, SocketAsyncEventArgs e)
        {
            // Determine which type of operation just completed and call the associated handler.
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    this.ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    this.ProcessSend(e, true);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }
        }
        /// <summary>
        /// 处理socket错误
        /// </summary>
        /// <param name="e"></param>
        private void ProcessError(SocketAsyncEventArgs e)
        {
            try
            {
                Socket s = e.UserToken as Socket;
                IPEndPoint localEp = s.LocalEndPoint as IPEndPoint;

                this.CloseClientSocket(s, e);

                string outStr = String.Format("{0} SOCKET ERR {1}, IP {2}, 操作 {2}。", iocp_name,
                    (Int32)e.SocketError, localEp, e.LastOperation);

                LogHelp.Error(outStr);
            }
            catch (Exception ex)
            {
                //AdoInsertTemp.AddServerErrorLog("处理socket错误:[IcopServer代码行号431]" + ex.Message); 
                LogHelp.Error(ex.ToString());
            }
        }
        /// <summary>
        /// 关闭socket连接
        /// </summary>
        /// <param name="e">SocketAsyncEventArg associated with the completed send/receive operation.</param>
        private void CloseClientSocket(SocketAsyncEventArgs e)
        {
            Socket s = e.UserToken as Socket;
            this.CloseClientSocket(s, e);
        }
        /// <summary>
        /// accept 操作完成时回调函数
        /// </summary>
        /// <param name="sender">Object who raised the event.</param>
        /// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param>
        private void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
        {
            this.ProcessAccept(e);
        }

        private void CloseClientSocket(Socket s, SocketAsyncEventArgs e)
        {
            try
            {
                if (s != null && this.numConnectedSockets > 0)
                {
                    Interlocked.Decrement(ref this.numConnectedSockets);
                    lock (_lock)
                    {
                        lock (_lock)
                        {
                            for (int i = 0; i < sockets.Count; i++)
                            {
                                if (s == sockets[i].socket)
                                {
                                    string outStr = String.Format("{0} {1} 断开, 共有 {2} 个连接。",
                                       iocp_name,
                                       sockets[i].ipaddr,
                                       this.numConnectedSockets);
                                    if (CloseSocketCallBack != null)
                                    {
                                        CloseSocketCallBack(sockets[i].socket);
                                    }
                                    LogHelp.Error(outStr);
                                    sockets.RemoveAt(i);
                                    break;
                                }
                            }
                        }
                    }

                    try
                    {
                        s.Shutdown(SocketShutdown.Send);
                    }
                    catch (Exception)
                    {
                        // Throw if client has closed, so it is not necessary to catch.
                    }
                    finally
                    {
                        s.Close();
                    }

                    // SocketAsyncEventArg 对象被释放，压入可重用队列。
                    this.ioContextPool.Push(e);
                 
                }
            }
            catch (Exception ex)
            {
                //AdoInsertTemp.AddServerErrorLog("sokect关闭:[IcopServer代码行号467]" + ex.Message);                 
                //主动关闭必须要提示
                LogHelp.Error(ex.ToString());
            }
        }

    }
}

